﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;  
using System.Data.Objects;
using System.Data.Entity.Design.PluralizationServices;
using System.Globalization;
using Portfolio.Repository.Interfaces;
using System.Data;
using Portfolio.Entities;

namespace Portfolio.Repository.Common
{
    /// <summary>
	/// A generic repository for working with data in the database
	/// </summary>
	/// <typeparam name="T">A POCO that represents an Entity Framework entity</typeparam>
    public class GenericRepository : IGenericRepository
    {   
	    /// <summary>
	    /// The context object for the database
        /// </summary>
	    private ObjectContext _context;
        private readonly PluralizationService _pluralizer;

        /// <summary>
        /// The IObjectSet that represents the current entity.
        /// </summary>
        //private IObjectSet<T> _objectSet;

        /// <summary>
        /// Initializes a new instance Portfolio3Entities of the GenericRepository class
        /// </summary>
        /// <param name="context">The Entity Framework ObjectContext</param>
        public GenericRepository() : this(new Portfolio3Entities())
        {            
            _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
        }
        
        /// <summary>
	    /// Initializes a new instance of the GenericRepository class
	    /// </summary>
        /// <param name="context">The Entity Framework ObjectContext</param>
	    public GenericRepository(ObjectContext context)
	    {
	        _context = context;
            _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));

            //_objectSet = _context.CreateObjectSet<T>();
	    }

        //DRUGA METODA WYKORZYSTUJACA BEZPOSREDNIO LINQ
        //public IQueryable<Portfolio.Entities.ArticleT> GetAllArticles()
        //{
        //    Portfolio.Entities.Portfolio3Entities _context2 = new Portfolio.Entities.Portfolio3Entities();
        //    1.ZWYKLE ZAPYTANIE LINQ
        //    var linq = from article in _context2.ArticleTs
        //          where article.SecurityPropertyId == securityId            
        //          select article;
        //
        //    2.SKŁADNIA LAMBDA
        //    return _context2.ArticleTs
        //    .Where(article => article.Text == "a")
        //    .OrderBy(article => article.SeqArticleCategory);
        //}
	    
        private string GetEntityName<TEntity>() where TEntity : class	    
        {
            //return string.Format("ObjectContext.{0}", _pluralizer.Pluralize(typeof(TEntity).Name));
            return string.Format("{0}", _pluralizer.Pluralize(typeof(TEntity).Name));
	    }

	    /// <summary>
	    /// Gets all records as an IQueryable
	    /// </summary>
	    /// <returns>An IQueryable object containing the results of the query</returns>
        public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
	    {
	        var entityName = GetEntityName<TEntity>();
	        return _context.CreateQuery<TEntity>(entityName);            
	    }

        /// <summary>
        /// The IObjectSet that represents the current entity.
        /// WYkorzystuje fetch i ustwiam objectset, najlepiej byloby dopisac do klasy zmienna prywatna, ale nie mam przy klasie okreslonego TEntity
        /// </summary>
        public IQueryable<TEntity> Fetch<TEntity>() where TEntity : class
        {
            IObjectSet<TEntity> _objectSet = _context.CreateObjectSet<TEntity>();
            return _objectSet;
        }
        //użycie
        //using (ArticleCategoriesDDLRepository r = new ArticleCategoriesDDLRepository(_context))
        //{

        //    var linq = from x in r.Fetch<ArticleCategoriesDDLSearchV>()
        //               select new
        //               {
        //                   x.DepthLevel
        //               };
        //}

        /// <summary>
	    /// Gets all records as an IEnumberable
	    /// </summary>
	    /// <returns>An IEnumberable object containing the results of the query</returns>
        public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
	    {
            return GetQuery<TEntity>().AsEnumerable();
	    }
	 
	    /// <summary>
	    /// Finds a record with the specified criteria
	    /// </summary>
	    /// <param name="predicate">Criteria to match on</param>
	    /// <returns>A collection containing the results of the query</returns>
        public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
	    {
            return GetQuery<TEntity>().Where(predicate).AsEnumerable();
	    }
	 
	    /// <summary>
	    /// Gets a single record by the specified criteria (usually the unique identifier)
	    /// </summary>
	    /// <param name="predicate">Criteria to match on</param>
	    /// <returns>A single record that matches the specified criteria</returns>
        public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
	    {
            return GetQuery<TEntity>().Single<TEntity>(predicate);
	    }
	 
	    /// <summary>
	    /// The first record matching the specified criteria
	    /// </summary>
	    /// <param name="predicate">Criteria to match on</param>
	    /// <returns>A single record containing the first record matching the specified criteria</returns>
        public TEntity First<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
	    {
            return GetQuery<TEntity>().Where(predicate).FirstOrDefault();
	    }
	 
	    /// <summary>
	    /// Deletes the specified entitiy
	    /// </summary>
	    /// <param name="entity">Entity to delete</param>
	    /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
        public void Delete<TEntity>(TEntity entity) where TEntity : class
	    {
            object _deletedEntity;

            if (entity == null)
	        {
	            throw new ArgumentNullException("entity");
	        }
            
            EntityKey key = _context.CreateEntityKey(GetEntityName<TEntity>(), entity);
            if (_context.TryGetObjectByKey(key, out _deletedEntity))
            {
                _context.DeleteObject(_deletedEntity);
                _context.SaveChanges();
            }
            
            //_context.DeleteObject(entity);
	    }
	 
	    /// <summary>
	    /// Adds the specified entity
	    /// </summary>
	    /// <param name="entity">Entity to add</param>
	    /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
        public int Add<TEntity>(TEntity entity) where TEntity : class
	    {	       
            if (entity == null)
	        {
	            throw new ArgumentNullException("entity");
	        }           
            _context.AddObject(GetEntityName<TEntity>(), entity);
            _context.SaveChanges();
            EntityKey _key = _context.CreateEntityKey(GetEntityName<TEntity>(), entity);
            return (int)_key.EntityKeyValues[0].Value;
            
	    }
	 
	    /// <summary>
	    /// Attaches the specified entity
	    /// </summary>
	    /// <param name="entity">Entity to attach</param>
        public void Attach<TEntity>(TEntity entity) where TEntity : class
	    {
            

            //ustawiony jest w fetch
            //_objectSet.Attach(entity);
	    }

        public int Update<TEntity>(TEntity entity) where TEntity : class
        {
            object _updateEntity;
            EntityKey key = _context.CreateEntityKey(GetEntityName<TEntity>(), entity);

            if (_context.TryGetObjectByKey(key, out _updateEntity))
            {
                //stare _context.ApplyPropertyChanges(key.EntitySetName, entity);
                _context.ApplyCurrentValues(key.EntitySetName, entity);
            }
            _context.SaveChanges();            
            return (int)key.EntityKeyValues[0].Value;

            //ReleaseObjectContextIfNotReused();
        }

	    /// <summary>
	    /// Saves all context changes
	    /// </summary>
	    public int SaveChanges()
        {            
            return _context.SaveChanges();
	    }
	 
	    /// <summary>
	    /// Saves all context changes with the specified SaveOptions
	    /// </summary>
	    /// <param name="options">Options for saving the context</param>
	    public void SaveChanges(SaveOptions options)
	    {
	        _context.SaveChanges(options);
	    }
	 
	    /// <summary>
	    /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
	    /// </summary>
	    public void Dispose()
	    {
	        Dispose(true);
	        GC.SuppressFinalize(this);
	    }
	 
	    /// <summary>
	    /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
	    /// </summary>
	    /// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
	    protected virtual void Dispose(bool disposing)
	    {
	        if (disposing)
	        {
	            if (_context != null)
	            {
	                _context.Dispose();
	                _context = null;
	            }
	        }
	    }
	}
}
